package com.lagou.niancheng.core.parse;

import com.lagou.niancheng.core.config.Configuration;
import com.lagou.niancheng.core.exceptions.LagouMybatisException;
import com.lagou.niancheng.core.result.DefaultResultSetConverter;
import com.lagou.niancheng.core.result.ResultSetConverter;
import com.lagou.niancheng.core.session.ExecutorFactory;
import com.lagou.niancheng.core.session.defaults.DefaultExecutorFactory;
import com.lagou.niancheng.core.transaction.DefaultTransactionManagerFactory;
import com.lagou.niancheng.core.transaction.TransactionManagerFactory;
import com.lagou.niancheng.core.utils.ReflectionUtil;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.beans.PropertyVetoException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.List;
import java.util.Properties;

public class XMLConfigBuilder {

    private Configuration configuration;

    public XMLConfigBuilder() {
        this.configuration = new Configuration();
    }

    /**
     * 该方法就是使用dom4j对配置文件进行解析，封装Configuration
     */
    @SuppressWarnings("unchecked")
    public Configuration parseConfig(InputStream inputStream) throws DocumentException, PropertyVetoException {
        Document document = new SAXReader().read(inputStream);
        //获取configuration标签
        Element rootElement = document.getRootElement();

        // 获取property
        Properties properties = getProperties(rootElement);

        // 设置数据源
        buildDataSource(properties);

        // 设置事务管理器工厂
        buildTransactionManagerFactory(properties);

        // 设置执行器工厂
        buildExecutorFactory(properties);

        // 设置结果集转换器
        buildResultSetConverter(properties);

        // 解析mapper配置文件
        parseMapperFile(rootElement);

        return configuration;
    }


    @SuppressWarnings("unchecked")
    private void parseMapperFile(Element rootElement) throws DocumentException {
        //mapper.xml解析: 拿到路径--字节输入流---dom4j进行解析
        List<Element> mapperList = rootElement.selectNodes("//mapper");
        for (Element element : mapperList) {
            String mapperPath = element.attributeValue("resource");
            InputStream resourceAsSteam = Resources.getResourceAsSteam(mapperPath);
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
            xmlMapperBuilder.parse(resourceAsSteam);
        }
    }

    @SuppressWarnings("unchecked")
    private Properties getProperties(Element rootElement) {
        List<Element> list = rootElement.selectNodes("//property");
        Properties properties = new Properties();
        for (Element element : list) {
            String name = element.attributeValue("name");
            String value = element.attributeValue("value");
            properties.setProperty(name, value);
        }
        return properties;
    }


    private void buildResultSetConverter(Properties properties) {
        String resultSetConverterName = properties.getProperty("resultSetConverter");
        if (resultSetConverterName == null) {
            configuration.setResultSetConverter(new DefaultResultSetConverter());
            return;
        }
        Class converterClass = ReflectionUtil.getClass(resultSetConverterName);
        if (ResultSetConverter.class.isAssignableFrom(converterClass)) {
            ResultSetConverter resultSetConverter = (ResultSetConverter) ReflectionUtil.getClassInstance(converterClass);
            configuration.setResultSetConverter(resultSetConverter);
        } else {
            throw new LagouMybatisException(MessageFormat.format("{0} is not a ResultSetConverter",
                    resultSetConverterName));
        }
    }


    private void buildExecutorFactory(Properties properties) {
        String executorFactoryName = properties.getProperty("executorFactory");
        if (executorFactoryName == null) {
            configuration.setExecutorFactory(new DefaultExecutorFactory());
            return;
        }
        Class executorClass = ReflectionUtil.getClass(executorFactoryName);
        if (ExecutorFactory.class.isAssignableFrom(executorClass)) {
            ExecutorFactory executorFactory = (ExecutorFactory) ReflectionUtil.getClassInstance(executorClass);
            configuration.setExecutorFactory(executorFactory);
        } else {
            throw new LagouMybatisException(MessageFormat.format("{0} is not a ExecutorFactory",
                    executorFactoryName));
        }
    }


    private void buildDataSource(Properties properties) throws PropertyVetoException {
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
        comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
        comboPooledDataSource.setUser(properties.getProperty("username"));
        comboPooledDataSource.setPassword(properties.getProperty("password"));
        configuration.setDataSource(comboPooledDataSource);
    }

    private void buildTransactionManagerFactory(Properties properties) {
        String managerFactoryName = properties.getProperty("transactionManagerFactory");
        String autoCommitValue = properties.getProperty("autoCommit");
        if (autoCommitValue == null) {
            configuration.setAutoCommit(false);
        } else {
            configuration.setAutoCommit(Boolean.parseBoolean(autoCommitValue));
        }
        if (managerFactoryName == null) {
            configuration.setTransactionManagerFactory(new DefaultTransactionManagerFactory());
        } else {
            Class<?> factoryClass = ReflectionUtil.getClass(managerFactoryName);
            if (TransactionManagerFactory.class.isAssignableFrom(factoryClass)) {
                TransactionManagerFactory factory = (TransactionManagerFactory) ReflectionUtil.getClassInstance(factoryClass);
                configuration.setTransactionManagerFactory(factory);
            } else {
                throw new LagouMybatisException(MessageFormat.format("{0} is not a TransactionManagerFactory",
                        managerFactoryName));
            }
        }

    }


}
